﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MoneyWentWhere.SimpleTextFile;
using MoneyWentWhere.Test.MwwSimple;
using System.Collections;
using FileHelpers;
using MoneyWentWhere.OutputHelpers;
using MoneyWentWhere.Utils;
using MoneyWentWhere.SimpleTextFile.ExchangeRates;
using System.IO;

namespace MoneyWentWhere.MwwSimpleTest
{
    [TestFixture]
    public class Reports
    {
        #region Utils
        const String DefaultCurrency = "USD";
        static readonly DateTime TxReliabilityCutoffDate = new DateTime(2008, 8, 19);

        MwwDataFile _data;

        MwwDataFile Data
        {
            get 
            {
                if (_data == null)
                {
                    String inputFile = OldExcelFormatConvert.OutputFile;
                    if (!File.Exists(inputFile))
                    {
                        Assert.Ignore("File does not exist: " + inputFile);
                    }
                    
                    _data = new MwwDataFile();
                    _data.ReadFile(inputFile);
                }
                return _data;
            }
        }

        ExchangeRateTable _xrTable;
        ExchangeRateTable XrTable
        {
            get
            {
                if (_xrTable == null)
                {
                    LocalExchangeRateSource xrSrc = new LocalExchangeRateSource(Data);
                    _xrTable = new ExchangeRateTable(xrSrc);
                }
                return _xrTable;
            }
        }
        #endregion

        #region Accounts
        [Test]
        public void AcountBalances()
        {
            var rows = Data.Accounts
                .Select( ac => new AccountBalanceRow(ac, XrTable))
                .OrderBy( x => -x.UsdBalance );

            var rowsWithTotal = rows.Concat(
                new AccountBalanceRow()
                {
                    Name = "---TOTAL",
                    UsdBalance = rows.Sum(x => x.UsdBalance),
                });

            Console.WriteLine(TablePrinter.Print(rowsWithTotal));
        }

        class AccountBalanceRow
        {
            [DisplayIgnore]
            public Account Account;

            public String Name;
            public decimal Balance;
            public decimal UsdBalance;

            public AccountBalanceRow() { }

            public AccountBalanceRow(Account ac, ExchangeRateTable xrTable)
            {
                Account = ac;
                Name = ac.Name;
                Balance = ac.Balance;

                ExchangeRate xr = xrTable.GetRate(ac.Currency, DefaultCurrency, DateTime.Now);
                if (xr != null)
                {
                    UsdBalance = ac.Balance * xr.Rate;
                }
            }
        }

        [Test]
        public void LocalExchangeRates() 
        {
            LocalExchangeRateSource xrSrc = new LocalExchangeRateSource(Data);
            ExchangeRateTable xr = new ExchangeRateTable(xrSrc);

            Console.WriteLine(xr.ToDisplayString());
        }

        [Test]
        public void AccountBalancesByYear()
        {
            LocalExchangeRateSource xrSrc = new LocalExchangeRateSource(Data);
            ExchangeRateTable xrTable = new ExchangeRateTable(xrSrc);

            // All the relevant years
            var years = Data.Accounts.SelectMany( ac => ac.Transactions.Select(x => x.Date.Year)).Distinct();

            var balances = Data.Accounts.Select(ac => GetBalancesDCPerYear(ac, xrTable, years));

            var totals = new AccountPerYearRow() { Name = "--TOTAL" };
            totals.Values = balances
                 .SelectMany(row => row.Values) // flat list of (year, balance) pairs
                 .GroupBy(kvp => kvp.Key) // grouping of (year, list(balance)) 
                 .ToDictionary(group => group.Key,
                               group => group.Sum(kvp => kvp.Value)); // apply the function

            var result = balances.Concat(totals);

            // Get the change between years
            var change = new AccountPerYearRow() { Name = "--Change" };
            change.Values = totals.Values
                .ToDictionary( x => x.Key,
                               x => x.Value - totals.Values.GetOrDefault(x.Key - 1));
            result = result.Concat(change);

            Console.WriteLine(TablePrinter.Print(result));
        }

        // TODO: Generalize ...
        AccountPerYearRow GetBalancesDCPerYear(Account account, ExchangeRateTable xrTable, IEnumerable<int> years)
        {
            var row = new AccountPerYearRow() { Name = account.Name };

            row.Values = years
                .ToDictionary( year => year,
                               year => GetBalanceDC(account, xrTable, new DateTime(year, 1, 1)));
            return row;
        }

        decimal GetBalanceDC(Account account, ExchangeRateTable xrTable, DateTime date)
        {
            DateTime endDate = date.AddYears(1);
            ExchangeRate rate = xrTable.GetRate(account.Currency, DefaultCurrency, endDate, searchAfterDate: true);

            if (rate == null)
            {
                Console.WriteLine("No exchange rate from " + account.Currency + " to " + DefaultCurrency + " at " + endDate);
                return 0;
            }

             return account.BalanceAt(endDate) * rate.Rate;
        }

        class AccountPerYearRow
        {
            public String Name;
            public Dictionary<int, decimal> Values = new Dictionary<int, decimal>();
        }
        #endregion

        #region Transactions
        const int LargeTxMin = 250;

        [Test]
        public void LargeTransactionsBeforeCutoff()
        {
            var result = GetTransactionsOverThreshold(LargeTxMin, beforeCutoff: true)
                .Where(x => x.Date <= TxReliabilityCutoffDate)
                .OrderBy(x => x.Date);

            TablePrinter.ConsolePrint(result);   
        }

        [Test]
        public void LargeTransactionsByYear()
        {
            var largestTx = GetTransactionsOverThreshold(LargeTxMin)
                .OrderBy(x => -x.Date.Year)
                .ThenBy(x => x.DcAmount > 0)
                .ThenBy(x => -Math.Abs(x.DcAmount));

            TablePrinter.ConsolePrint(largestTx);
        }

        IEnumerable<TxRow> GetTransactionsOverThreshold(decimal absMinAmount, bool beforeCutoff = false)
        {
            return Data.Accounts
                .SelectMany(x => x.Transactions)
                .Where(x => !(x is Transfer) && (beforeCutoff || x.Date > TxReliabilityCutoffDate))
                // Convert amounts, select ones that are large enough
                .Select(x => new TxRow(x, XrTable))
                .Where(x => Math.Abs(x.DcAmount) >= absMinAmount);
        }

        public class TxRow
        {
            [DisplayIgnore]
            public Transaction Tx;

            public int Year;

            public DateTime Date;
            public String AccountName;
            public decimal Amount;
            public string Currency;
            public decimal DcAmount;
            public String DcCurrency = DefaultCurrency;
            public String Comment;
            public String Tags;

            public TxRow(Transaction tx, ExchangeRateTable xrTable)
            {
                Tx = tx;

                Year = tx.Date.Year;

                Date = tx.Date;
                AccountName = tx.Account.Name;
                Amount = tx.Amount;
                Currency = tx.Account.Currency;

                ExchangeRate xr = xrTable.GetRate(Currency, DefaultCurrency, tx.Date, searchAfterDate: true); 
                DcAmount = Amount * xr.Rate;

                Comment = tx.Comment.TrimOrPad(30, Alignment.Left);
                Tags = tx.Tags + (tx is Reconcile ? " RECONCILE" : "");
            }
        }

        #endregion
    }

}
